package org.zoomdev.zoom.dao.struct.impl;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.zoomdev.zoom.common.io.Io;
import org.zoomdev.zoom.common.utils.Classes;
import org.zoomdev.zoom.dao.Dao;
import org.zoomdev.zoom.dao.DaoException;
import org.zoomdev.zoom.dao.driver.SqlDriver;
import org.zoomdev.zoom.dao.struct.ColumnMeta;
import org.zoomdev.zoom.dao.struct.DatabaseStruct;
import org.zoomdev.zoom.dao.struct.TableMeta;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public abstract class DatabaseStructImpl implements DatabaseStruct {

    protected final static Log log = LogFactory.getLog(Dao.class);

    protected final String name;
    private final Dao dao;
    private final SqlDriver driver;
    private final Map<String, TableMeta> cache = new ConcurrentHashMap<>();

    public DatabaseStructImpl(Dao dao, String name) {
        this.dao = dao;
        this.driver = dao.sqlDriver();
        this.name = name;
    }


    public TableMeta getTableMeta(String table) {
        assert (table != null);
        return cache.computeIfAbsent(table.toLowerCase(), k -> {
            try {
                return dao.execute(connection -> {
                    StringBuilder sb = new StringBuilder();
                    sb.append("SELECT * FROM ");
                    driver.protect(sb, table);
                    sb.append(" WHERE 1=2");
                    ResultSet rs = null;
                    PreparedStatement statement = null;
                    try {
                        statement = connection.prepareStatement(sb.toString());
                        rs = statement.executeQuery();
                        ResultSetMetaData data = rs.getMetaData();
                        List<ColumnMeta> columnMetas = new ArrayList<>(data.getColumnCount());
                        TableMeta tableMeta = new TableMeta(table, columnMetas);
                        for (int i = 1, c = data.getColumnCount(); i <= c; ++i) {
                            ColumnMeta column = new ColumnMeta(tableMeta,
                                    data.getColumnType(i),
                                    Classes.forName(data.getColumnClassName(i)),
                                    table,
                                    data.getColumnName(i));
                            columnMetas.add(column);
                        }

                        try {
                            fill(tableMeta, connection);
                        } catch (DaoException ex) {
                            log.warn("无法从数据库源获取表结构" + tableMeta.getName());
                        }
                        return tableMeta;
                    } finally {
                        Io.close(statement);
                        Io.close(rs);
                    }
                });
            } catch (DaoException e) {
                throw new DaoException("获取数据表信息失败:" + table + " 表信息不存在", e.getCause());
            }
        });
    }

    protected abstract TableMeta fill(TableMeta meta, Connection connection) throws SQLException;

}
